"""
@project: vcat
@author: kang 
@github: https://github.com/fsksf 
@since: 2021/12/22 10:06 PM
"""
from abc import ABC
import copy
from datetime import datetime
from typing import List, Dict, Union
from vcat.core.constant import (
    DataLevel,
)
from vcat.core.obj import (
    TickData,
    BarData,
    BarGenArrayParams,
    TradeData,
    PositionData,
    OrderRequest,
    InitParams
)
from vcat.core.constant import (
    Offset, Direction, OrderType,
    RunningMod
)
from vcat.core.array_manager import XArrayManager
from vcat.core.guard_left import BarGenerator


class BaseTemplate:
    """
    策略基础类，所有策略必须继承于此类
    __init__ 重载（重写） 必须调用super
    bar事件处理函数命名规则：on_<level><window>_bar
    如： on_1minute_bar, on_15minute_bar, on_1hour_bar, on_1day_bar
    """

    author = 'kangyuqiang'
    brief = '基础模板'
    start_dt: datetime = None
    end_dt: datetime = None

    gen_params_list: List[BarGenArrayParams] = [
        BarGenArrayParams(bar_level=DataLevel.minute, bar_interval=1, arr_size=60),
    ]
    # 初始化需要输入的params, 前端根据这个生成input列表
    init_params = {}
    # 策略实例要展示的参数
    display_params = []

    def __init__(self,
                 name: str,
                 contract_ids,
                 start_dt=None,
                 end_dt=None,
                 params=None
                 ):
        self._name = name
        self._contract_ids = list(set(contract_ids))
        self.start_dt = start_dt
        self.end_dt = end_dt
        self._mod = mod
        self._bar_managers: Dict[str, XArrayManager] = {}
        for bar_gen in self.gen_params_list:
            self._bar_managers[bar_gen.interval_level_key] = XArrayManager(
                size=bar_gen.arr_size,
                level=bar_gen.bar_level,
                interval=bar_gen.bar_interval
            )
        self._inited_history = False
        self._inited_array = False
        self._running = False
        self.level: DataLevel = DataLevel.day
        self._pos: Dict[str, PositionData] = {}
        self._params = params or {}
        self.parse_params()
        self.init()

    def parse_params(self):
        # 重写此方法配置对 额外多有参数的 params的处理
        for k, v in self._params.items():
            try:
                old = getattr(self, k)
            except AttributeError:

                self.write_log(f'警告：类对象{self.__class__.__name__} 没有预设属性 {k}, 设置后可能对程序无效')
            print(k, v)
            setattr(self, k, v)

    def init(self):
        for bg in self.gen_params_list:
            if bg.bar_level.value < self.level.value:
                self.level = bg.bar_level

    @property
    def mod(self):
        return self._mod

    @property
    def inited_history(self):
        # 是否完成加载历史
        return self._inited_history

    @property
    def inited_array(self):
        # array是否初始化完成
        if self._inited_array:
            return self._inited_array
        elif all([xar.inited for xar in self._bar_managers.values()]):
            self._inited_array = True
        return self._inited_array

    @property
    def running(self):
        return self._running

    @property
    def name(self):
        return self._name

    @property
    def sid(self):
        return f'{self.__class__.__name__}.{self.name}'

    @property
    def contract_ids(self):
        return self._contract_ids

    @property
    def params(self):
        return self.gen_params_list

    def get_method(self, name):
        return getattr(self, name)

    def get_history(self, contract_ids: List[str], start_dt, end_dt, count=None, freq=DataLevel.minute):
        raise NotImplementedError

    def get_history_bar(self, contract_ids: List[str], start_dt, end_dt, count=None, freq=DataLevel.minute):
        raise NotImplementedError

    def subscribe(self, contract_ids: List[str]):
        raise NotImplementedError

    def add_bar_generator(self, b_params: BarGenArrayParams, contract_ids: list):
        raise NotImplementedError

    def _on_history_bar(self, bar: BarData):
        """
        :param bar:
        :return:
        """

        xam = self._bar_managers.get(bar.interval_level_key)
        if xam is None:
            return
        xam.update_bar(bar)

    def inner_on_init_tick(self, tick: TickData):
        pass

    def inner_on_init_bar(self, bar: BarData):
        if bar:
            self._on_history_bar(bar)
        else:
            self._inited_history = True
            self.on_init()

    def update_trade(self, trade: TradeData):
        pos = self._pos.get(trade.cid, 0)

        if trade.direction == Direction.LONG:
            pos += trade.volume
        else:
            pos -= trade.volume
        self._pos[trade.cid] = pos

    def on_init(self):
        pass

    def start(self):
        if not self.inited_array:
            self.write_log('请先初始化策略')
            return
        self._running = True

    def inner_on_tick(self, tick: TickData):
        self.on_tick(tick)

    def on_tick(self, tick: TickData):
        pass

    def inner_on_mid_bar(self, bar: BarData):
        """
        回测过程中需要保证每个时刻Bar列表中 只有最后一个Bar触发策略的on_bar事件。
        此函数不会触发策略的on_bar函数
        :param bar:
        :return:
        """
        if not self.inited_history:
            # 历史未加载，不可在array内添加合成bar
            return
        xam = self._bar_managers[bar.interval_level_key]
        xam.update_bar(bar)

    def inner_on_bar(self, bar: BarData):
        """
        内部调用接口， 更新 bar_manager, 此函数会触发财策略的on_bar函数
        :param bar:
        :return:
        """
        self.inner_on_mid_bar(bar)
        if not (self.inited_array and self.running):
            # 没有初始化完成或者没有在运行，不可进入用户代码
            return
        self.on_bar(bar)

    def on_bar(self, bar: BarData):
        """
        bar事件
        :param bar:
        :return:
        """
        pass

    def long(self,
             cid: str,
             price: float,
             volume: Union[float, int],
             order_type: OrderType):

        """
        开多
        :param cid:
        :param price:
        :param volume:
        :param order_type:
        :return:
        """

        self._send_order(
            cid=cid,
            price=price,
            volume=volume,
            order_type=order_type,
            direction=Direction.LONG,
            offset=Offset.OPEN
        )

    def sell(self,
             cid: str,
             price: float,
             volume: Union[float, int],
             order_type: OrderType):

        """
        平多
        :param cid:
        :param price:
        :param volume:
        :param order_type:
        :return:
        """

        self._send_order(
            cid=cid,
            price=price,
            volume=volume,
            order_type=order_type,
            direction=Direction.LONG,
            offset=Offset.CLOSE
        )

    def short(self,
              cid: str,
              price: float,
              volume: Union[float, int],
              order_type: OrderType):

        """
        开空
        :param cid:
        :param price:
        :param volume:
        :param order_type:
        :return:
        """

        self._send_order(
            cid=cid,
            price=price,
            volume=volume,
            order_type=order_type,
            direction=Direction.SHORT,
            offset=Offset.OPEN
        )

    def cover(self,
              cid: str,
              price: float,
              volume: Union[float, int],
              order_type: OrderType):
        """
        平空
        :param cid:
        :param price:
        :param volume:
        :param order_type:
        :return:
        """
        self._send_order(
            cid=cid,
            price=price,
            volume=volume,
            order_type=order_type,
            direction=Direction.SHORT,
            offset=Offset.CLOSE
        )

    def _send_order(self,
                    cid,
                    direction,
                    offset,
                    price,
                    volume,
                    order_type: OrderType):
        req = OrderRequest(
            strategy_ins_name=self.name,
            cid=cid,
            direction=direction,
            offset=offset,
            price=price,
            volume=volume,
            order_type=order_type,
            code=None,
            exchange=None,
            contract_type=None,
            broker_name=None
        )
        self.send_order(req)

    def send_order(self, req: OrderRequest):
        raise NotImplementedError

    def write_log(self, msg):
        """

        :param msg:
        :return:
        """
        pass

    def send_msg(self, msg):
        """
        发送消息
        :param msg:
        :return:
        """
        pass

    def stop(self):
        self._running = False

    @classmethod
    def meta(cls):
        return {
            'cls': cls.__name__,
            'author': cls.author,
            'brief': cls.brief
        }


    @classmethod
    def get_init_params(cls):
        params = cls.init_params.copy()
        extra = {
            'name': InitParams(key='name', name='策略实例名称', dtype=str, default=''),
            'contract_ids': InitParams(key='contract_ids', name='合约列表', dtype=str, default=''),
            'start_dt': InitParams(key='start_dt', name='开始时间', dtype=str, default=''),
            'end_dt': InitParams(key='end_dt', name='结束时间', dtype=str, default='20990101')
        }
        params.update(extra)
        return [x.to_dict() for x in params.values()]